home *** CD-ROM | disk | FTP | other *** search
/ La Bible Des... Jeux / La Bible des... Jeux.iso / Utilitaires / CaRMSAPA / Sources / CaRMSAPA.c next >
Encoding:
C/C++ Source or Header  |  1995-12-15  |  20.3 KB  |  729 lines  |  [TEXT/CWIE]

  1. //
  2. //    CaRMSAPA.c
  3. //    (Change and Restore Monitor Settings - Application Pseudo Alias)
  4. //
  5. //    A tiny application that acts like an alias to another application,
  6. //    except that it changes the screen depth before launching it, and 
  7. //    restores the depth when that application quits.
  8. //
  9. //    This is designed to be as small and light as possible.
  10. //    Errors reported with the Notification Manager.
  11. //    Target application is set with drag and drop.
  12. //    'hfnd' resource says what the current target is.
  13. //
  14. //    It was supposed to be a background-only application,
  15. //    but it didn't work out that way. Calls to 
  16. //    SetDepth() don't like being Background-Only.
  17. //    There is still lots of BOA code though.
  18. //
  19. //    by James Jennings
  20. //    Started October 29, 1995
  21. //
  22.  
  23. //#include "APA Testing.h"    // routines for testing only
  24.  
  25. #include "CaRMSAPA.h"
  26. #include <Palettes.h>
  27.  
  28. Boolean gDone = false;
  29. Boolean gRunning = false;
  30. long gSleepVal = 6000;    // Large, since we don't interact with the user.
  31. short gApplResFileRef = 0;
  32.  
  33. const short  aliasID         = 128;
  34. const OSType ScreenStateType= 'ScnD';
  35. const short  ScreenStateID    = 128;
  36. const short  errorMsgListID    = 128;
  37. const short  finderHelpStrID= 128;
  38. const short  finderHelpStrTemplateID = 129;
  39. const short  strUnknownID    = 130;
  40.  
  41. ScreenStateRecord savedScreenState;
  42. ProcessSerialNumber savedProcessSerialNumber;
  43.  
  44. // Background only applications have a small stack (2K) so we
  45. // make some Str255's global.
  46. Str255 errStr;    // used in Error();
  47. Str255 fdrStr;    // used in RememberEverything()
  48.  
  49. // The notification manager record used in Message()
  50. NMRec notifyRecord;
  51.  
  52. // controls kind of code in RecallTargetApp()
  53. #define BACKGROUND_ONLY
  54.  
  55. void main(void)
  56. {
  57.     long response;
  58.     OSErr iErr;
  59.     Boolean hasAppleEvents;
  60.     AEEventHandlerUPP RunAEHandlerUPP;
  61.     AEEventHandlerUPP QuitAEHandlerUPP;
  62.     AEEventHandlerUPP OpenAEHandlerUPP;
  63.     AEEventHandlerUPP ChildDiedAEHandlerUPP;
  64.     // Toolbox initialization.
  65.     // QUESTION: Do I need to InitGraf() to support my GDevice calls?
  66.     InitGraf((Ptr) &qd.thePort);
  67.  
  68.     if ( IsBackgroundOnly() ) {
  69.         // I've been crashing. In case it's because the stacks too small, increase it.
  70.         IncreaseApplicationStack(22*1024);
  71.     } else {
  72.         InitFonts();
  73.         // NIM: Processes pg 1-7 says that background-only can't call InitWindows()
  74.         InitWindows();
  75.         InitMenus();
  76.         TEInit();
  77.         InitDialogs(0L);
  78.         InitCursor();
  79.     }
  80.  
  81.     MaxApplZone();
  82.     // Remember the application's resource fork
  83.     gApplResFileRef = CurResFile();
  84.     
  85.     // Do we have the necessary system features?
  86.     if ( ! ICanRun() ) {
  87.         // If not, abort. We can't do anything.
  88.         Error(errMissingSystemFeature);
  89.         return;
  90.     }
  91.  
  92.     // Install AppleEvent Handlers.
  93.     RunAEHandlerUPP = NewAEEventHandlerProc (&RunAEHandler);
  94.     QuitAEHandlerUPP = NewAEEventHandlerProc (&QuitAEHandler);
  95.     OpenAEHandlerUPP = NewAEEventHandlerProc (&OpenAEHandler);
  96.     ChildDiedAEHandlerUPP = NewAEEventHandlerProc (&ChildDiedAEHandler);
  97.     if (!RunAEHandlerUPP || !OpenAEHandlerUPP || !ChildDiedAEHandlerUPP ) {
  98.         // Couldn't allocate UPP's. Abort.
  99.         Error(errUnexpected);
  100.         return;
  101.     }
  102.     iErr = AEInstallEventHandler(
  103.                 kCoreEventClass,     // will be called for any event class
  104.                 kAEOpenApplication, // will be called for any event ID
  105.                 RunAEHandlerUPP,     // the handler
  106.                 0,                    // handler ref con
  107.                 false);                // not a system handler    
  108.     if (iErr!=noErr) { Error(errUnexpected); return; }
  109.     iErr = AEInstallEventHandler(
  110.                 kCoreEventClass,     // will be called for any event class
  111.                 kAEOpenDocuments,    // will be called for any event ID
  112.                 OpenAEHandlerUPP,     // the handler
  113.                 0,                    // handler ref con
  114.                 false);                // not a system handler    
  115.     if (iErr!=noErr) { Error(errUnexpected); return; }
  116.     iErr = AEInstallEventHandler(
  117.                 kCoreEventClass,     // will be called for any event class
  118.                 kAEApplicationDied, // will be called for any event ID
  119.                 ChildDiedAEHandlerUPP,     // the handler
  120.                 0,                    // handler ref con
  121.                 false);                // not a system handler    
  122.     if (iErr!=noErr) { Error(errUnexpected); return; }
  123.     
  124. #if 0    // insert a bunch of testing routines here
  125.     TestStrings();
  126.     TestScreenState();
  127.     return;
  128. #endif
  129.     
  130.     // our event loop
  131.      while (!gDone) {
  132.         EventRecord theEvent;
  133.         Boolean gotEvent = WaitNextEvent(everyEvent,
  134.                                     &theEvent,
  135.                                     gSleepVal,
  136.                                     (RgnHandle)nil);
  137.         if (gotEvent && theEvent.what == kHighLevelEvent) {
  138.             // This is a background only app. We only do AppleEvents
  139.             iErr = AEProcessAppleEvent (&theEvent);
  140.         }
  141.     }
  142. }
  143.  
  144. /******************** from develop issue 9 ************************************/
  145.  
  146. /*
  147. **    Increase the space allocated for the application stack.
  148. **
  149. **    Warning: SetApplLimit always sets the stack to at least as large as the 
  150. **        default stack for the machine (8K on machines with original QuickDraw,  
  151. **        24K on machines with Color QuickDraw), so the application partition 
  152. **        must be large enough to accommodate an appropriate stack and heap.
  153. **    
  154. **        Call this only once, at the beginning of the application.
  155. **
  156. **    Another warning:
  157. **        Don't bother trying to set the stack size to something lower than 24K.
  158. **        If SetApplLimit is called to do this, it will silently lower ApplLimit
  159. **        to a 24K stack.
  160. */
  161. OSErr IncreaseApplicationStack(Size incrementSize)
  162. {
  163.     OSErr result;
  164.  
  165.     /* Increase the stack size by lowering the heap limit. */
  166.     SetApplLimit((Ptr) ((unsigned long) GetApplLimit() - incrementSize));
  167.     result = MemError();
  168.     if ( result == noErr )
  169.         MaxApplZone();
  170.         
  171.     return ( result );
  172. }
  173.  
  174. /*****************************************************************************/
  175.  
  176. inline Boolean EqualPSN(const ProcessSerialNumber *sn1, const ProcessSerialNumber *sn2)
  177. {
  178.     return sn1->highLongOfPSN==sn2->highLongOfPSN && sn1->lowLongOfPSN==sn2->lowLongOfPSN;
  179. }
  180.  
  181. ////////////////////////////////////////
  182. //
  183. //    AppleEvent Handlers
  184. //
  185. ////////////////////////////////////////
  186. pascal OSErr RunAEHandler (AppleEvent * theEvent, AppleEvent * theReply, long refcon) 
  187. {    // Set up the screen depth and launch the child application.
  188.     ScreenStateRecord sd;
  189.     FSSpec file;
  190.     LaunchParamBlockRec launchRec;
  191.     
  192.     // there shouldn't be any required parameters
  193.     OSErr iErr = GotRequiredParam(theEvent);
  194.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  195.     
  196.     if (gRunning) {
  197.         // we only respect the first "run" event
  198.         Error(errLaunchedMoreThanOnce);
  199.         return errAEEventNotHandled;
  200.     }
  201.     
  202.     // get the desired screen state
  203.     sd = RecallScreenState();
  204.     if ( ! ScreenStateAvail(sd) ) { 
  205.         Error(errCantSetScreenState);
  206.         return errAEEventNotHandled; 
  207.     }
  208.     
  209.     // get the target app's FSSpec
  210.     iErr = RecallTargetApp(&file);
  211.     if (noErr!=iErr) {
  212.         Error(errCantFindApplication);
  213.         return iErr;
  214.     }
  215.     
  216.     // If target is already running, we can't launch it.
  217.     if ( IsAppRunning(&file) ) {
  218.         Error(errTargetAlreadyRunning);
  219.         return errAEEventNotHandled;
  220.     }
  221.     
  222.     // Passed all critical tests. Change the screen if necessary.
  223.     savedScreenState = GetScreenState();
  224.     if ( !EqualScreenState( sd, savedScreenState) ) {
  225.         SetScreenState(sd);
  226.     } else {
  227.         // the screen doesn't need changing: we don't need to hang around
  228.         gDone = true;
  229.     }
  230.     
  231.     // launch application...
  232.     launchRec.launchBlockID = extendedBlock;
  233.     launchRec.launchEPBLength = extendedBlockLen;
  234.     launchRec.launchFileFlags = 0;
  235.     launchRec.launchControlFlags = launchContinue | launchNoFileFlags;
  236.     launchRec.launchAppSpec = &file;
  237.     launchRec.launchAppParameters = 0;
  238.     iErr = LaunchApplication(&launchRec);
  239.     if (iErr != noErr) {
  240.         Error(errUnexpected);
  241.         return iErr;
  242.     }
  243.     // Save the returned process serial number.
  244.     savedProcessSerialNumber = launchRec.launchProcessSN;
  245.     
  246.     gRunning = true;
  247.     return noErr;
  248. }
  249.  
  250. pascal OSErr QuitAEHandler (AppleEvent * theEvent, AppleEvent * theReply, long refcon) 
  251. {    // Quit. Don't do anything special since this is not normally used.
  252.     OSErr iErr = GotRequiredParam(theEvent);
  253.     if (iErr==noErr)
  254.         Error(errUnexpected);
  255.     gDone = true;    // quit no matter what
  256.     return iErr;
  257. }
  258.  
  259. pascal OSErr OpenAEHandler (AppleEvent * theEvent, AppleEvent * theReply, long refcon) 
  260. {    // Remember an APPL that has been dropped on us.
  261.     AEDesc docList = {typeNull, 0};
  262.     long numberOfItems;
  263.     FSSpec theFSS;
  264.     AEKeyword keywd;
  265.     DescType returnedType;
  266.     Size actualSize;
  267.     OSErr iErr;
  268.     
  269.     gDone = true;    // quit when done, no matter what
  270.     
  271.     // Extract the doc list.
  272.     iErr = AEGetParamDesc(theEvent, keyDirectObject, typeAEList, &docList);
  273.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  274.     
  275.     // Have we gotten all the required parameters?
  276.     iErr = GotRequiredParam(theEvent);
  277.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  278.     
  279.     // How many docs?
  280.     iErr = AECountItems(&docList,&numberOfItems);
  281.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  282.     
  283.     if (numberOfItems!=1) {
  284.         Error(errTooManyItemsToOpen);
  285.         return errAEEventNotHandled;
  286.     }
  287.     
  288.     // Extract a FSSpec
  289.     iErr = AEGetNthPtr( &docList, 1, typeFSS,
  290.                     &keywd, &returnedType, &theFSS,
  291.                     sizeof(theFSS), &actualSize);
  292.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  293.     
  294.     // Check the file type.
  295.     if (GetFileType(&theFSS) != 'APPL') {
  296.         Error(errDroppedFileOfWrongType);
  297.         return errAEEventNotHandled;
  298.     }
  299.     // Everything is hunky-dory
  300.     iErr = RememberEverything(&theFSS);
  301.     if (iErr!=noErr) { Error(errUnexpected); return iErr; }
  302.     
  303.     // fdrStr is set as a side effect of RememberEverything
  304.     Message(fdrStr);
  305.     return noErr;
  306. }
  307.  
  308. pascal OSErr ChildDiedAEHandler (AppleEvent * theEvent, AppleEvent * theReply, long refcon) 
  309. {    // Restore the screen depth and quit.
  310.     OSErr iErr;
  311.     DescType typeCode;
  312.     Size actualSize;
  313.     long childDiedErrorNumber;
  314.     ProcessSerialNumber psn;
  315.     
  316.     gDone = true;    // quit, no matter what
  317.     // There are two required parameters, keyErrorNumber and keyProcessSerialNumber
  318.     iErr = AEGetParamPtr(theEvent, keyErrorNumber, 
  319.                     typeLongInteger, &typeCode, &childDiedErrorNumber, 
  320.                     sizeof(long), &actualSize);
  321.     if (iErr!=noErr) { 
  322.         Error(errUnexpected); 
  323.         return iErr; 
  324.     }
  325.     iErr = AEGetParamPtr(theEvent, keyProcessSerialNumber, 
  326.                     typeProcessSerialNumber, &typeCode, &psn, 
  327.                     sizeof(ProcessSerialNumber), &actualSize);
  328.     if (iErr!=noErr) { 
  329.         Error(errUnexpected); 
  330.         return iErr; 
  331.     }
  332.     iErr = GotRequiredParam(theEvent);
  333.     if (iErr!=noErr) return iErr;
  334.     
  335.     // Just as a lark, make sure that the child process is the same
  336.     if ( !EqualPSN(&savedProcessSerialNumber, &psn) )  { 
  337.         Error(errUnexpected); 
  338.         return iErr; 
  339.     }
  340.     // restore the screen state
  341.     SetScreenState(savedScreenState);
  342.     
  343.     return noErr;
  344. }
  345.  
  346. ////////////////////////////////////////
  347. //
  348. //    AppleEvent Utilities
  349. //
  350. ////////////////////////////////////////
  351. OSErr GotRequiredParam(AppleEvent *theEvent)
  352. {
  353.     DescType returnedType;
  354.     Size actualSize;
  355.     OSErr iErr;
  356.     iErr = AEGetAttributePtr( theEvent,
  357.             keyMissedKeywordAttr,
  358.             typeWildCard, &returnedType,    // dummy returned type
  359.             0,                        // ignore the missed keyword list
  360.             0,                        // buffer size (there is no buffer)
  361.             &actualSize);            // dummy returned size
  362.     // If no missed keywords are found, then noErr: all required param's have been got.
  363.     if (iErr == errAEDescNotFound)
  364.         iErr = noErr;
  365.     else
  366.         iErr = errAEParamMissed;
  367.         
  368.     return iErr;
  369. }
  370.  
  371. ////////////////////////////////////////
  372. //
  373. //    Screen Property Utilities
  374. //
  375. ////////////////////////////////////////
  376. Boolean EqualScreenState(ScreenStateRecord ss1, ScreenStateRecord ss2)
  377. {
  378.     return ss1.isColor == ss2.isColor && ss1.theDepth==ss2.theDepth;
  379. }
  380.  
  381. ScreenStateRecord GetScreenState(void)
  382. {
  383.     ScreenStateRecord theState;
  384.     // get the color bit
  385.     GDHandle gdh = GetMainDevice();
  386.     theState.isColor = TestDeviceAttribute(gdh, gdDevType);
  387.     // get the depth (look at the GDevice's pixel map record)
  388.     theState.theDepth = (**((**gdh).gdPMap)).pixelSize;
  389.     
  390.     return theState;
  391. }
  392.  
  393. Boolean ScreenStateAvail(ScreenStateRecord theState)
  394. {
  395.     return HasDepth( GetMainDevice(), theState.theDepth, gdDevType, theState.isColor);
  396. }
  397.  
  398. void SetScreenState(ScreenStateRecord theState)
  399. {
  400.     OSErr iErr;
  401.     iErr = SetDepth(    GetMainDevice(),     // the GDevice to change
  402.                         theState.theDepth,    // the new depth
  403.                         1<<gdDevType,        // the whichFlags value for setting b&w/color
  404.                         theState.isColor);    // 0 = b&w, 1 = color
  405.     if (iErr!=noErr) {
  406.         Error(errUnexpected);
  407.         return;
  408.     }
  409. }
  410.  
  411. void RememberScreenState(ScreenStateRecord theState)
  412. {
  413.     OSErr iErr;
  414.     ScreenStateHdl sdh = (ScreenStateHdl)Get1Resource(ScreenStateType, ScreenStateID);
  415.     if (sdh) {
  416.         **sdh = theState;
  417.         ChangedResource((Handle)sdh);
  418.     } else // The resource should have been there.
  419.         Error(errUnexpected);
  420. }
  421.  
  422. ScreenStateRecord RecallScreenState(void)
  423. {
  424.     ScreenStateHdl sdh = (ScreenStateHdl)Get1Resource(ScreenStateType, ScreenStateID);
  425.     if (sdh) return **sdh;
  426.     // we shouldn't get here, but if we do, return something reasonable
  427.     Error(errUnexpected);
  428.     return GetScreenState();
  429. }
  430.  
  431.  
  432. ////////////////////////////////////////
  433. //
  434. //    String Utilities
  435. //        Pretty kludgy, but it should handle two-byte script systems.
  436. //
  437. ////////////////////////////////////////
  438. inline void StringCopy(Str255 src, Str255 dst)
  439. {
  440.     BlockMove(src,dst,src[0]+1);
  441. }
  442. inline void StringAppend(Str255 add, Str255 dest)
  443. {
  444.     BlockMove(add+1,dest+dest[0]+1,add[0]);
  445.     dest[0] += add[0];
  446. }
  447. void StringTemplate(Str255 tmp, Str255 sub1, Str255 sub2)
  448. {    // substitute
  449.     Handle t,s;
  450.     Size len;
  451.     if (tmp==0) return; // sanity check
  452.     
  453.     // convert string to a handle
  454.     if ( noErr!=PtrToHand(tmp+1, &t,tmp[0]) ) return;
  455.     
  456.     if (sub1) {
  457.         if ( noErr!=PtrToHand(sub1+1, &s, sub1[0]) ) goto abort;
  458.         ReplaceText(t, s, "\p^0");    // note: we ignore the number of substitutions
  459.         DisposeHandle(s);
  460.     }
  461.     if (sub2) {
  462.         if ( noErr!=PtrToHand(sub2+1, &s,sub2[0]) ) goto abort;
  463.         ReplaceText(t, s, "\p^1");    // note: we ignore the number of substitutions
  464.         DisposeHandle(s);
  465.     }
  466. abort:
  467.     len = GetHandleSize(t);
  468.     if (len>255) len=255;    // clip the string
  469.     BlockMove(*t,tmp+1,len);
  470.     tmp[0] = len;
  471.     DisposeHandle(t);
  472. }
  473.  
  474. void ScreenStateToString(ScreenStateRecord sd, Str63 str)
  475. {
  476.     StringHandle stringHdl, colorHdl;
  477.     Str63 num;
  478.     const short baseID = 1000;
  479.     const short grayID = 998, colorID = 999;
  480.     short depthID = baseID + sd.theDepth;
  481.     str[0] = 0;    // zero the string
  482.     
  483.     colorHdl = GetString( sd.isColor? colorID : grayID );
  484.     stringHdl = GetString(depthID);    // depth as string: 256, thousands, millions, etc
  485.     
  486.     if (stringHdl) {
  487.         StringCopy(*stringHdl,str);
  488.         if (sd.theDepth==1) return;
  489.         HLock((Handle)colorHdl);
  490.         StringTemplate(str,0,*colorHdl);
  491.         HUnlock((Handle)colorHdl);
  492.     } else {
  493.         // didn't find it. Build it.
  494.         stringHdl = GetString(baseID);
  495.         if (stringHdl==0) return;    // still can't find it: abort
  496.         StringCopy(*stringHdl,str);
  497.         NumToString(sd.theDepth,num);
  498.         HLock((Handle)colorHdl);
  499.         StringTemplate(str,num,*colorHdl);
  500.         HUnlock((Handle)colorHdl);
  501.     }
  502. }
  503.  
  504. void AliasToFileName(AliasHandle theAlias, Str63 name)
  505. {
  506.     StringHandle sh;
  507.     if (GetAliasInfo(theAlias, asiAliasName, name)==noErr) return;
  508.     // sensible default behaviour
  509.     sh = GetString(strUnknownID);
  510.     if (sh)
  511.         StringCopy(*sh,name);
  512.     else
  513.         name[0] = 0;
  514. }
  515.  
  516. void MakeFinderHelpString(Str255 s)
  517. {
  518.     AliasHandle ah;
  519.     ScreenStateHdl sdh;
  520.     StringHandle sh;
  521.     Str63 screen, file;    // Small for BOA
  522.     
  523.     ah = (AliasHandle)Get1Resource(rAliasType, aliasID);
  524.     sdh = (ScreenStateHdl)Get1Resource(ScreenStateType, ScreenStateID);
  525.     AliasToFileName(ah, file);
  526.     ScreenStateToString(**sdh, screen);
  527.     sh = GetString(finderHelpStrTemplateID);
  528.     if (sh) {
  529.         StringCopy(*sh, s);
  530.         StringTemplate(s, screen, file);
  531.     }
  532. }
  533.  
  534. ////////////////////////////////////////
  535. //
  536. //    Alias Utilities
  537. //
  538. ////////////////////////////////////////
  539. OSType GetFileType(FSSpec *spec)
  540. {
  541.     FInfo fndrInfo;
  542.     OSErr err = HGetFInfo (spec->vRefNum, spec->parID, spec->name, &fndrInfo);
  543.     if (err) return '???\?';
  544.     return fndrInfo.fdType;
  545. }
  546.  
  547. Boolean EqualFSSpec(FSSpec *f1, FSSpec *f2);    // inline eventually
  548. Boolean EqualFSSpec(FSSpec *f1, FSSpec *f2)
  549. {    // are two FSSpecs equal?
  550.     if (f1->vRefNum != f2->vRefNum)    return false;
  551.     if (f1->parID != f2->parID)        return false;
  552.     return EqualString(f1->name,f2->name,false,false);
  553. }
  554.  
  555. OSErr RememberTargetApp(FSSpec* f)
  556. {    // save an alias to the file in the resource fork
  557.     AliasHandle ah, ahr;
  558.     Size len;
  559.     OSErr iErr;
  560.     
  561.     iErr = NewAlias(0,f,&ah);
  562.     if (noErr!=iErr) return iErr;
  563.     if (ah) {
  564.         // remove the old resource, if any
  565.         ahr = (AliasHandle) Get1Resource(rAliasType,aliasID);
  566.         if (ahr) RemoveResource((Handle)ahr);
  567.         // add the alias to the resource
  568.         AddResource((Handle)ah, rAliasType, aliasID, 0);
  569.         iErr = ResError();
  570.     }
  571.     return iErr;
  572. }
  573.  
  574. OSErr RecallTargetApp(FSSpec *f)
  575. {    // recover the file spec from the alias resource
  576.     AliasHandle ah;
  577.     Boolean wasChanged = false;
  578.     OSErr iErr = noErr;
  579. #ifdef BACKGROUND_ONLY
  580.     Boolean needsUpdate = false;
  581.     short aliasCount = 1;    // only search for one match
  582. #endif
  583.     
  584.     ah = (AliasHandle) Get1Resource(rAliasType,aliasID);
  585. #ifdef BACKGROUND_ONLY
  586.     // Note: ResolveAlias() can't be used in a BOA. Use MatchAlias() instead.
  587.     iErr = MatchAlias(0,// fromFile for relative path
  588.             // rulesMask == no user interaction + fast search
  589.             kARMNoUI + kARMSearch,
  590.             ah,         // the alias to resolve
  591.             &aliasCount,// allowed number of results == 1
  592.             f,            // aliasList, 
  593.             &needsUpdate,// needsUpdate
  594.             0,            // no filter function, 
  595.             0            // no yourDataPtr for the filter function
  596.             );
  597.     if (iErr!=noErr) return iErr;
  598.     
  599.     if (needsUpdate) { // save any changes
  600.         iErr = UpdateAlias(0, f, ah, &wasChanged);
  601.         if (iErr==noErr && wasChanged)    // save any changes
  602.             ChangedResource((Handle)ah);
  603.     }
  604. #else
  605.     iErr = ResolveAlias(0, ah, f, &wasChanged);
  606.     if (noErr == iErr) {
  607.         if (wasChanged)    // save any changes
  608.             ChangedResource((Handle)ah);
  609.     }
  610. #endif
  611.     return iErr;
  612. }
  613.  
  614. OSErr RememberEverything(FSSpec *spec)
  615. {
  616.     OSErr iErr;
  617.     StringHandle sh;
  618.     
  619. //    UseResFile(gApplResFileRef);    // shouldn't be necessary.
  620.     iErr = RememberTargetApp(spec);
  621.     if ( noErr != iErr ) return iErr;
  622.     RememberScreenState(GetScreenState());
  623.     MakeFinderHelpString(fdrStr);
  624.     sh = GetString(finderHelpStrID);
  625.     if (sh) {
  626.         SetHandleSize((Handle)sh, fdrStr[0]+1 );
  627.         if (MemError()!=noErr) return MemError();
  628.         StringCopy(fdrStr,*sh);
  629.         ChangedResource((Handle)sh);
  630.     }
  631.     UpdateResFile(gApplResFileRef);
  632.     return noErr;
  633. }
  634.  
  635. Boolean IsAppRunning(FSSpec *theApp)
  636. {    // is the app already launched?
  637.     FSSpec procFSS;
  638.     OSErr iErr;
  639.     ProcessSerialNumber psn;
  640.     ProcessInfoRec pir;
  641.     
  642.     // loop through the processes, looking for ours
  643.     psn.highLongOfPSN = 0;
  644.     psn.lowLongOfPSN = kNoProcess;
  645.     pir.processInfoLength = sizeof(ProcessInfoRec);
  646.     pir.processName = 0;    // don't want the name
  647.     pir.processAppSpec = &procFSS;    // file spec of the process
  648.     
  649.     for (iErr = GetNextProcess(&psn); iErr!=procNotFound; iErr = GetNextProcess(&psn)) {
  650.         // loop through the processes, looking for something that matches our file
  651.         iErr = GetProcessInformation(&psn, &pir);
  652.         if (iErr==noErr)
  653.             if ( EqualFSSpec(theApp, &procFSS) )
  654.                 return true;
  655.     }
  656.     // didn't find it (thank goodness)
  657.     return false;
  658. }
  659.  
  660. ////////////////////////////////////////
  661. //
  662. //    Error Messages
  663. //
  664. ////////////////////////////////////////
  665. void Error(ErrorCode code)
  666. {    // Report an error
  667.     GetIndString(errStr, errorMsgListID, code);
  668.     Message(errStr);
  669.     gDone = true;
  670. }
  671.  
  672. static void Resp(NMRec *rec)
  673. {
  674.     OSErr iErr = NMRemove(rec);    // remove the notification
  675.     rec->nmRefCon = 0;            // stop waiting for a response
  676. }
  677.  
  678. void Message(Str255 str)
  679. {    // Send message to the user using the notification manager.
  680.     OSErr iErr;
  681. //    DebugStr(str);
  682.     notifyRecord.qType        = nmType;
  683.     notifyRecord.nmMark        = 0;        // no mark
  684.     notifyRecord.nmIcon        = 0;        // no icon
  685.     notifyRecord.nmSound    = 0;        // no sound
  686.     notifyRecord.nmStr        = str;        // Don't release this memory!
  687. //    notifyRecord.nmResp = (void*)-1;    // -1 = automaticly remove from queue when done
  688.                             // why do we need this cast?
  689.     notifyRecord.nmResp     = (void*)Resp;        // response proc
  690.     notifyRecord.nmRefCon    = 1;        // Resp Proc sets this to 0 when done.
  691.     
  692.     iErr = NMInstall(¬ifyRecord);
  693.     if (iErr!=noErr) return;    // sanity check
  694.     // Hang around until the notification is aknowledged.
  695.     while (notifyRecord.nmRefCon) {
  696.         EventRecord theEvent;
  697.         WaitNextEvent(0/*no events*/, &theEvent, 6/*sleep*/, 0);
  698.     }
  699. }
  700.  
  701. Boolean ICanRun(void)
  702. {    // Test to see if the system supports the things we need.
  703.     Boolean hasAppleEvents, hasNotification;
  704.     long response;
  705.     OSErr iErr;
  706.     
  707.     // test for AppleEvents
  708.     iErr = Gestalt(gestaltAppleEventsAttr, &response);
  709.     hasAppleEvents = (iErr == noErr) && (response & 1 << gestaltAppleEventsPresent);
  710.     if (!hasAppleEvents) return false;
  711.     
  712.     // test for notification manager
  713.     iErr = Gestalt(gestaltNotificationMgrAttr, &response);
  714.     hasNotification = (iErr == noErr) && (response & 1 << gestaltNotificationPresent);
  715.     if (!hasNotification) return false;
  716.     
  717.     return true;
  718. }
  719.  
  720. Boolean IsBackgroundOnly(void)
  721. {    // Examine the SIZE -1 to see if we're background only.
  722.     typedef struct { short flags; long prefered; long minimum; } SIZERec, **SIZEHdl;
  723.     const long BOMask = 0x0400;
  724.     SIZEHdl h = (SIZEHdl) GetResource('SIZE', -1);
  725.     if (h==0) return false;
  726.     
  727.     return ((**h).flags & BOMask) != 0;
  728. }
  729.